package com.lcp.se.service;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.lcp.se.entity.data.AppletUser;
import com.lcp.se.entity.result.Result;
import com.lcp.se.entity.vo.AuthVO;
import com.lcp.se.mapper.AppletUserMapper;
import com.lcp.se.util.AesCbcUtil;
import com.lcp.se.util.ProjectConstants;
import com.lcp.se.util.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;

/**
 * @author lcp
 * @since 2020/7/16 10:53
 */
@Service
public class AppletUserService {
    private final Logger log = LoggerFactory.getLogger(AppletUserService.class);

    @Value("${mini-program.app-id}")
    private String appId;

    @Value("${mini-program.app-secret}")
    private String appSecret;

    @Value("${mini-program.auth-code2-session-url}")
    private String authCode2SessionUrl;

    @Value("${mini-program.user-auth-info-key-redis}")
    private String authInfoKey;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private AppletUserMapper appletUserMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 登录
     */
    public Result<AuthVO> login(String code) {
        // 请求微信的服务器，获取 session_key，openid
        String url = String.format(authCode2SessionUrl, appId, appSecret, code);
        String result;
        try {
            result = restTemplate.getForObject(url, String.class);
        } catch (RestClientException e) {
            log.info("连接微信授权服务器超时：" + e.getMessage());
            return Result.failure("登陆失败");
        }
        JSONObject authData = JSONObject.parseObject(result);
        String sessionKey = authData.getString("session_key");
        String openId = authData.getString("openid");

        // 判断用户是否注册，未注册则自动注册，注册过则获取 userType
        Integer userType = null;
        AppletUser appletUser = appletUserMapper.selectById(openId);
        if (appletUser == null) {
            appletUserMapper.insert(new AppletUser(openId, new Date()));
            appletUser = new AppletUser().setNickName(null).setAvatarUrl(null).setPhoneNumber(null);
        } else {
            userType = appletUser.getUserType();
        }

        // 删除 openId 在 redis 中对应的旧登录信息
        try {
            redisTemplate.opsForHash().delete(authInfoKey, openId);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 生成 sessionId 返回给小程序
        String sessionId = SecureUtil.md5(sessionKey + openId);
        // 以 openId 作为 key，将 sessionId + separator + sessionKey 作为 value 存入 redis
        String authInfo = sessionId + ProjectConstants.SEPARATOR_AUTH + sessionKey;
        redisTemplate.opsForHash().put(authInfoKey, openId, authInfo);

        if (appletUser.getPhoneNumber() == null || appletUser.getPhoneNumber().equals("")) {
            return Result.success("登陆成功", new AuthVO(sessionId, userType, openId,
                    appletUser.getNickName(), appletUser.getAvatarUrl(), false));
        } else {
            return Result.success("登陆成功", new AuthVO(sessionId, userType, openId,
                    appletUser.getNickName(), appletUser.getAvatarUrl(), true));
        }

    }

    /**
     * 更新手机号
     */
    public Result<String> updatePhoneNumber(String encryptedData, String iv, String sessionId, String openId) {
        if (sessionId == null || sessionId.equals("")) {
            return new Result<>(false, StatusCode.ERROR, "更新失败");
        }

        // 根据 openId 获取 sessionKey
        String sessionKey = Objects.requireNonNull(
                redisTemplate.opsForHash().get(authInfoKey, openId)).toString().split(ProjectConstants.SEPARATOR_AUTH)[1];

        // 解密用户数据
        try {
            String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
            String phoneNumber = JSONObject.parseObject(result).getString("purePhoneNumber");
            int i = appletUserMapper.updateById(new AppletUser().setUserOpenId(openId).setPhoneNumber(phoneNumber));
            if (i == 1) {
                return Result.success("更新成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.failure("更新失败");
    }

    /**
     * 更新用户信息
     */
    public Result<String> updateUserInfo(String encryptedData, String iv, String sessionId, String openId) {
        if (sessionId == null || sessionId.equals("")) {
            return Result.failure("更新失败");
        }

        // 根据 openId 获取 sessionKey
        String sessionKey = Objects.requireNonNull(
                redisTemplate.opsForHash().get(authInfoKey, openId)).toString().split(ProjectConstants.SEPARATOR_AUTH)[1];

        // 解密用户数据
        try {
            String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
            JSONObject data = JSONObject.parseObject(result);
            int i = appletUserMapper.updateById(new AppletUser().setUserOpenId(openId)
                    .setNickName(data.getString("nickName"))
                    .setGender(data.getInteger("gender"))
                    .setLanguage(data.getString("language"))
                    .setCity(data.getString("city"))
                    .setProvince(data.getString("province"))
                    .setCountry(data.getString("country"))
                    .setAvatarUrl(data.getString("avatarUrl")));
            if (i == 1) {
                return Result.success("更新成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.failure("更新失败");
    }

    /**
     * 更新用户类型
     */
    public Result<String> updateUserType(Integer userType, String openId) {
        int i = appletUserMapper.updateById(new AppletUser().setUserOpenId(openId).setUserType(userType));
        if (i == 1) {
            return new Result<>(true, StatusCode.OK, "更新成功");
        }
        return new Result<>(false, StatusCode.ERROR, "更新失败");
    }
}
